在昨天的文章中,我們整合了 Loguru 與 Sentry,為系統建立了即時可觀測性與自動告警機制。
當系統穩定運行在雲端並陸續開放給親朋好友使用後,除了系統有沒有掛掉之外,身為獨立開發者最關心的莫過於第二個靈魂拷問:
「這隻 LINE 機器人每個月到底要花我多少錢?免費額度用完後會不會突然收到天價帳單?」
一個優秀的工程專案,不只要把功能做出來,更要在**「使用者體驗」與「維運預算」**之間取得絕佳平衡。
今天 Day 27,我們將全面盤點這套 AI 美食推薦架構中的主要成本來源,透過真實數據分析 Gemini 3.5 Flash-Lite 與 Google Maps Places API 的花費結構,並分享 4 個立竿見影的成本收斂與最佳化技巧!
在我們的架構中,除了 Render、Zeabur 與 Upstash 提供充裕的免費層(Free Tier)之外,真正會隨使用量動態計費的只有兩項:
為了讓機器人即使面對流量爆發也能安穩運行在免費額度內,我們在過去幾天陸續埋下了關鍵的「防禦性省錢架構」:
Google Places API (New) 採用「依索取欄位階層計費」。如果沒有加上 X-Goog-FieldMask 標頭,或者填入 *,Google 會回傳包含所有評分、完整營業時間甚至評論文字的高級資訊,以最貴的費率計價!
我們在 app/services/places.py 中明確宣告只索取必要欄位:
headers = {
"X-Goog-Api-Key": GOOGLE_MAPS_API_KEY,
"X-Goog-FieldMask": "places.id,places.displayName,places.formattedAddress,places.rating,places.priceLevel,places.editorialSummary"
}
熱門商圈(例如「板橋 甜點」、「西門町 日式」)的查詢重複率高達 40% ~ 60%。
我們設定了 6 小時的快取存活時間(TTL):
只要有人在 6 小時內問過相同商圈與料理類型,後續查詢全部由 Redis 記憶體快取秒回。
成果數據:以每日 100 次查詢為例,快取命中率約 50%,直接讓 Google Places API 呼叫次數腰斬 50%,同時平均延遲從 800ms 降至 10ms!
並不是所有的使用者操作都需要經過大語言模型:
傳送 GPS 位置訊息時(Day 16):使用者已經提供經緯度,n8n Switch 節點直接分流至微服務,完全跳過 Dify。
點擊 Quick Reply 抽籤時(Day 18):點擊「🎲 再抽一次」屬於明確的系統指令,微服務直接執行隨機選取,跳過 Dify。
點擊「❤️ 收藏」時(Day 19):LINE Postback 事件直接打到後端資料庫寫入,跳過 Dify。
💡 效果:只在「使用者輸入自訂口語」時才動用 Gemini 大腦,省下了超過 60% 的 LLM Token 消耗!
長篇大論的 System Prompt 會在每次對話重複消耗 Input Token。
我們在 Day 22 加固提示詞時,將範例精簡化,並直接規定「只輸出標準 JSON,不附帶任何客套話」,有效將 Input 抑制在 400 Tokens 內、Output 抑制在 60 Tokens 內,確保每一分 Token 都花在刀口上。
假設機器人擁有 50 位活躍好友,平均每人每月查詢 10 次(全月共 500 次查詢):
| 服務項目 | 免費額度 / 方案 | 實際預估消耗 | 實際預估支出 (USD) |
|---|---|---|---|
| Render Web Service | 每月 750 小時免費運行 | 運行 1 個 FastAPI 實例 | $0.00 |
| Zeabur / PaaS | 包含基礎免費額度 | 運行 1 個 n8n 實例 | $0.00 |
| Upstash Redis | 每日免費 10,000 次讀寫 | 每月約 3,000 次快取存取 | $0.00 |
| LINE Messaging API | 每月 200 則免費 Push,Reply 免費 | 150 則主動推播,其餘全用 Reply | $0.00 |
| Google Gemini 3.5 Flash-Lite | Google AI Studio 每月免費額度 | 約 10 萬 Tokens | $0.00 |
| Google Maps Places API | Google Cloud 平台常態每月免費抵扣 | 約 250 次(快取吸收後) | $0.00(全數由額度折抵) |
| 總計費用 | — | — | $0.00 元(完全免費運轉) |
為了防範萬一程式寫出無窮迴圈或突發流量導致扣款,務必在 Google Cloud Console 啟用預算警報(Budget Alerts):
1.前往 Google Cloud Console - 帳單儀表板。
2.進入「預算與快訊」➜ 點擊「設定預算」。
3.預算金額設定為 $1.00 美元(或你可接受的最低上限)。
4.設定快訊觸發比例:50%、90%、100%。
5.勾選「傳送電子郵件快訊給帳單管理員」。
6.這樣一來,只要當月花費接近或達到 1 美元,你就會在第一時間收到警告郵件,絕不當冤大頭!
在昨天的文章中,我們完成了系統營運成本與 Token 用量的全面盤點,透過 Field Masking、快取與邏輯分流,達成了零成本運轉的目標。
隨著專案進入尾聲,我們的程式庫已經累積了完整的微服務、資料庫模型、安全性防禦與測試程式碼。然而,在日常開發中,人總有疏忽的時候:
pytest 就直接推送到遠端倉庫為了避免「把有問題的程式碼推上正式環境」,現代軟體工程必備的核心利器就是 CI/CD(持續整合與持續交付)!
今天 Day 28,我們將利用 GitHub Actions 打造一條自動化流水線:每當我們執行 git push 或發起 Pull Request 時,雲端自動觸發虛擬機安裝環境、檢查程式碼規範並執行 Pytest 自動化測試,確保每一行交付的代碼都具備生產級的高品質!
GitHub Actions 是 GitHub 原生提供的 CI/CD 平台,其架構主要由以下元素組成:
.github/workflows/ 目錄下(使用 YAML 格式定義)。push(推送代碼)或 pull_request(合併請求)。[開發者推送代碼 (git push main)]
│
▼ (觸發 GitHub Actions)
[Ubuntu 雲端虛擬環境]
├─ 1. Checkout 原始碼 (actions/checkout)
├─ 2. 設定 Python 3.11 環境 (actions/setup-python)
├─ 3. 快取與安裝 requirements.txt 依賴
├─ 4. 程式碼靜態排版與語法檢查 (Flake8)
└─ 5. 執行自動化測試 (Pytest)
│
├───> ✅ 測試全數通過 ──> 標記綠色打勾(Render 放心拉取自動部署)
└───> ❌ 任一測試失敗 ──> 標記紅色叉叉並發送 Email 警報,終止交付!
在本地專案根目錄下建立 .github/workflows/ 資料夾層級,並新增 ci.yml 檔案:
agent-backend/
├── .github/
│ └── workflows/
│ └── ci.yml # GitHub Actions 流程定義檔
├── app/
├── tests/
├── Dockerfile
└── requirements.txt
撰寫 ci.yml 內容:
name: Food Agent Backend CI Pipeline
# 觸發條件:當 main 分支收到 push 或 pull_request 時自動執行
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
test-and-lint:
# 執行環境:最新穩定的 Ubuntu 虛擬機
runs-on: ubuntu-latest
steps:
# Step 1: 簽出(拉取)儲存庫程式碼
- name: Checkout Code
uses: actions/checkout@v4
# Step 2: 設定 Python 3.11 執行環境
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: "pip" # 自動快取 pip 下載套件以加速後續執行
# Step 3: 安裝專案相依套件與測試工具
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest pytest-asyncio
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
# Step 4: 執行靜態語法檢查 (Linting)
- name: Lint Code with Flake8
run: |
# 檢查關鍵語法錯誤或未定義變數(若有錯誤則終止)
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# 一般風格警告(可放寬行寬至 120 字元,避免中文字串過長報錯)
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=120 --statistics
# Step 5: 執行 Pytest 自動化測試
- name: Run Unit & Integration Tests
env:
# 提供虛擬的測試環境變數,避免讀取不到 Key 拋錯
GOOGLE_MAPS_API_KEY: "mock_test_key_for_ci"
LINE_CHANNEL_SECRET: "mock_channel_secret_for_ci"
ENVIRONMENT: "testing"
run: |
pytest -v --tb=short
為了避免 Flake8 對虛擬環境(.venv)或快取目錄進行無謂的掃描,我們在專案根目錄建立 .flake8 設定檔:
[flake8]
# 忽略檢查的目錄
exclude =
.git,
__pycache__,
.venv,
logs,
build,
dist
# 允許的最大單行字元長度
max-line-length = 120
# 忽略少數與 Black / 排版工具衝突的代碼
ignore = E203, W503
在將代碼推送到 GitHub 之前,我們先在本地確保測試全部通過:
pytest -v
確認所有測試呈現綠色 PASSED 後,使用 Git 提交並推送到 GitHub 遠端倉庫:
# 將新建立的 .github 目錄與設定檔加入暫存
git add .github/ .flake8
# 提交變更
git commit -m "ci: add GitHub Actions workflow for linting and pytest"
# 推送到 GitHub main 分支
git push origin main
1.打開瀏覽器,進入你的 GitHub 專案儲存庫(https://github.com/<你的帳號>/food-agent-backend)。
2.點擊頂部的 Actions 分頁標籤。
3.你會看到一個名為 Food Agent Backend CI Pipeline 的工作流程正在自動運轉。
4.點擊進入該次 Run,可以看到虛擬機各步驟的即時執行日誌:
Set up Python 3.11 ➜ 成功
Install Dependencies ➜ 成功安裝依賴套件
Lint Code with Flake8 ➜ 語法檢查通過
Run Unit & Integration Tests ➜ 5 個 Pytest 測試全數通過!
5.流程左側將亮起醒目的綠色打勾標章(Passed)!
為了驗證這條防線是否真的能幫我們把關,我們來做一個「破壞性實驗」:
1.刻意修改 app/core/security.py,故意改壞越獄字串的判斷邏輯。
2.執行 git commit -am "test: break security detector" 並 git push。
3.回到 GitHub Actions 頁面:
流程跑到 Step 5 時,tests/test_schemas.py 拋出 AssertionError。
GitHub Actions 立即亮起紅色叉叉(Failed)!
同時你的 GitHub 註冊信箱會收到警報通知:「Run failed: Food Agent Backend CI Pipeline」。
4.守門成果:
因為 CI 失敗,這份壞掉的代碼將無法通過 Pull Request 審查,有效杜絕了劣質代碼偷偷溜進生產線的風險!